<!--
***********************************************************************************************
CLRTest.Execute.Batch.targets

WARNING:  DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
          created a backup copy.  Incorrect changes to this file will make it
          impossible to load or build your projects from the command-line or the IDE.

This file contains the logic for providing Execution Script generation.

WARNING:   When setting properties based on their current state (for example:
           <Foo Condition="'$(Foo)'==''>Bar</Foo>).  Be very careful.  Another script generation
           target might be trying to do the same thing.  It's better to avoid this by instead setting a new property.

           Additionally, be careful with itemgroups.  Include will propagate outside of the target too!

***********************************************************************************************
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <!--
    Target: GetExecuteCmdFullPath
    Return Executed Cmd Relative Full Path
    We can use this target get its toRun Project's Cmd Full Path to avoid hard-code
  -->
  <Target
    Name="GetExecuteCmdFullPath"
    Returns="$(ExecuteCmdFullPath)">
    <PropertyGroup Condition="$(GenerateRunScript)">
        <ExecuteCmdFullPath>$(OutputPath)$(AssemblyName).cmd</ExecuteCmdFullPath>
    </PropertyGroup>
    <!-- In order to avoid the overhead of calling MSBuild (as it may result in chains of MSBuild calls) I am assuming the extension in this circumstance. -->
    <PropertyGroup Condition="$(CLRTestProjectToRun) != '' AND $(GenerateRunScript)">
      <ExecuteCmdFullPath>$(OutputPath)$([System.IO.Path]::GetFilenameWithoutExtension(&quot;$(CLRTestProjectToRun)&quot;)).dll</ExecuteCmdFullPath>
    </PropertyGroup>
    <PropertyGroup Condition="!$(GenerateRunScript)">
        <ExecuteCmdFullPath>$(OutputPath)$(AssemblyName).$(OutputType.ToLower())</ExecuteCmdFullPath>
    </PropertyGroup>
  </Target>

  <!--
  *******************************************************************************************
  TARGET: GenerateExecutionScriptInternal

  For tests that "run" we will generate an execution script that wraps any arguments or other
  goo.  This allows generated .lst files to be very simple and reusable to invoke any "stage"
  of test execution.

  Notice this is hooked up to run after targets that generate the stores that are marked with GenerateScripts metadata.
  Note also that this means it will run after the first of such targets.
  -->
  <Target Name="GenerateBatchExecutionScript"
    Inputs="$(MSBuildProjectFullPath)"
    Outputs="$(OutputPath)\$(AssemblyName).cmd"
    DependsOnTargets="$(BatchScriptSnippetGen);GetIlasmRoundTripBatchScript;GetSuperPMICollectionBatchScript">

    <Message Text="Project depends on $(_CLRTestToRunFileFullPath)." Condition="'$(_CLRTestNeedsProjectToRun)' == 'True'" />

    <PropertyGroup>
      <CoreRunArgs>@(RuntimeHostConfigurationOption -> '-p &quot;%(Identity)=%(Value)&quot;', ' ')</CoreRunArgs>
      <BatchCLRTestEnvironmentCompatibilityCheck Condition="'$(GCStressIncompatible)' == 'true'"><![CDATA[
$(BatchCLRTestEnvironmentCompatibilityCheck)
IF NOT "%DOTNET_GCStress%"=="" (
  ECHO SKIPPING EXECUTION BECAUSE DOTNET_GCStress IS SET
  popd
  Exit /b 0
)
      ]]></BatchCLRTestEnvironmentCompatibilityCheck>
      <BatchCLRTestEnvironmentCompatibilityCheck Condition="'$(UnloadabilityIncompatible)' == 'true'"><![CDATA[
$(BatchCLRTestEnvironmentCompatibilityCheck)
IF NOT "%RunInUnloadableContext%"=="" (
  ECHO SKIPPING EXECUTION BECAUSE the test is incompatible with unloadability
  popd
  Exit /b 0
)
      ]]></BatchCLRTestEnvironmentCompatibilityCheck>
      <BatchCLRTestEnvironmentCompatibilityCheck Condition="'$(TieringTestIncompatible)' == 'true'"><![CDATA[
$(BatchCLRTestEnvironmentCompatibilityCheck)
IF NOT "%RunTieringTest%"=="" (
  ECHO SKIPPING EXECUTION BECAUSE the test is incompatible with the tiering test harness
  popd
  Exit /b 0
)
      ]]></BatchCLRTestEnvironmentCompatibilityCheck>
      <BatchCLRTestEnvironmentCompatibilityCheck Condition="'$(JitOptimizationSensitive)' == 'true' And '$(RuntimeFlavor)' != 'mono' and '$(TestBuildMode)' != 'nativeaot'">
    <![CDATA[
$(BatchCLRTestEnvironmentCompatibilityCheck)
REM JitOptimizationSensitive includes our set of tests which cannot run under:
REM     JitMinOpts=1
REM     JitStress=*
REM     JitStressRegs=*
REM     TailcallStress=1
REM
REM TieredCompilation will use minopts. Therefore it is also included in this
REM set. Unlike the rest, TieredCompilation=0 should run the JitOptimizationSensitive
REM tests. The following cannot run the test.
REM
REM     TieredCompilation=1
REM     TieredCompilation=
IF "%DOTNET_JitStress%"=="" IF "%DOTNET_JitStressRegs%"=="" IF "%DOTNET_JITMinOpts%"=="" IF "%DOTNET_TailcallStress%"=="" goto :Compatible
  ECHO SKIPPING EXECUTION BECAUSE ONE OR MORE OF (DOTNET_JitStress, DOTNET_JitStressRegs, DOTNET_JITMinOpts, DOTNET_TailcallStress, DOTNET_TieredCompilation) IS SET
  popd
  Exit /b 0
:Compatible
IF "%DOTNET_TieredCompilation%"=="0" goto :TieredCompilationDisabledCorrectly
  ECHO SKIPPING EXECUTION BECAUSE DOTNET_TieredCompilation has not been disabled and this test is marked JitOptimizationSensitive
  popd
  Exit /b 0
:TieredCompilationDisabledCorrectly
      ]]></BatchCLRTestEnvironmentCompatibilityCheck>
      <BatchCLRTestEnvironmentCompatibilityCheck Condition="'$(HeapVerifyIncompatible)' == 'true'"><![CDATA[
$(BatchCLRTestEnvironmentCompatibilityCheck)
IF NOT "%DOTNET_HeapVerify%"=="" (
  ECHO SKIPPING EXECUTION BECAUSE DOTNET_HeapVerify IS SET
  popd
  Exit /b 0
)
      ]]></BatchCLRTestEnvironmentCompatibilityCheck>
      <BatchCLRTestEnvironmentCompatibilityCheck Condition="'$(IlasmRoundTripIncompatible)' == 'true'"><![CDATA[
$(BatchCLRTestEnvironmentCompatibilityCheck)
IF NOT "%RunningIlasmRoundTrip%"=="" (
  ECHO SKIPPING EXECUTION BECAUSE RunningIlasmRoundTrip IS SET
  popd
  Exit /b 0
)
      ]]></BatchCLRTestEnvironmentCompatibilityCheck>

      <BatchCLRTestEnvironmentCompatibilityCheck Condition="'$(SynthesizedPgoIncompatible)' == 'true'"><![CDATA[
$(BatchCLRTestEnvironmentCompatibilityCheck)
IF NOT "%CrossGen2SynthesizePgo"=="" (
  ECHO SKIPPING EXECUTION BECAUSE CrossGen2SynthesizePgo IS SET
  popd
  Exit /b 0
)
      ]]></BatchCLRTestEnvironmentCompatibilityCheck>

      <BatchCLRTestExitCodePrep Condition="$(_CLRTestNeedsToRun)">
        <![CDATA[
set CLRTestExpectedExitCode=$(CLRTestExitCode)
ECHO BEGIN EXECUTION
      ]]>
      </BatchCLRTestExitCodePrep>

      <BatchCLRTestArgPrep Condition=" '$(CLRTestExecutionArguments)'!='' "><![CDATA[
if not defined CLRTestExecutionArguments (set CLRTestExecutionArguments=$(CLRTestExecutionArguments) )
      ]]></BatchCLRTestArgPrep>

      <!-- By default, be prepared to do a full check -->
      <BatchCLRTestExitCodeCheck><![CDATA[
ECHO Expected: %CLRTestExpectedExitCode%
ECHO Actual: %CLRTestExitCode%
IF NOT "%CLRTestExitCode%"=="%CLRTestExpectedExitCode%" (
  ECHO END EXECUTION - FAILED
  ECHO FAILED
  popd
  Exit /b 1
) ELSE (
  ECHO END EXECUTION - PASSED
  ECHO PASSED
  popd
  Exit /b 0
)

:TakeLock
md %lockFolder%
IF NOT "!ERRORLEVEL!"=="0" (
  timeout /t 10 /nobreak
  goto :TakeLock
)
Exit /b 2


:ReleaseLock
if exist %lockFolder% rd /s /q %lockFolder%
Exit /b 0
      ]]></BatchCLRTestExitCodeCheck>
    </PropertyGroup>

    <ItemGroup Condition="$(_CLRTestNeedsToRun)">
      <Clean Include="$(OutputPath)\$(AssemblyName).cmd"/>

      <BatchCLRTestExecutionScriptArgument Include="debug">
        <HasParam>true</HasParam>
        <ParamName>debuggerFullPath</ParamName>
        <Command><![CDATA[
    IF EXIST "%2" (
        set _DebuggerFullPath=%2
        shift
    ) ELSE (
        ECHO The Debugger FullPath "%2" doesn't exist
        GOTO :USAGE
    )
        ]]></Command>
        <Description>Run testcases under debugger.</Description>
      </BatchCLRTestExecutionScriptArgument>

      <BatchCLRTestExecutionScriptArgument Include="e;env">
        <HasParam>true</HasParam>
        <ParamName>dotenvFileFullPath</ParamName>
        <Command><![CDATA[
    IF EXIST "%2" (
        set __DotEnvArg=-e %2
    ) ELSE (
        ECHO The dotenv file "%2" does not exist
        GOTO :USAGE
    )
        ]]></Command>
        <Description>A dotenv file to pass to corerun to set environment variables for the test run.</Description>
      </BatchCLRTestExecutionScriptArgument>

      <BatchCLRTestExecutionScriptArgument Include="coreroot">
        <HasParam>true</HasParam>
        <ParamName>CoreRootFullPath</ParamName>
        <Command><![CDATA[
    set CORE_ROOT=%2
        ]]></Command>
        <Description>Set CORE_ROOT to the specified value before running the test.</Description>
      </BatchCLRTestExecutionScriptArgument>

      <BatchCLRTestExecutionScriptArgument Include="usewatcher">
        <HasParam>false</HasParam>
        <Command><![CDATA[
    set /A _RunWithWatcher=1
        ]]></Command>
        <Description>Run the tests using the test watcher.</Description>
      </BatchCLRTestExecutionScriptArgument>
    </ItemGroup>

      <PropertyGroup>
          <ReflectionRootsXml>$(AssemblyName).reflect.xml</ReflectionRootsXml>
          <BatchLinkerTestLaunchCmds><![CDATA[
REM Linker commands

set LinkBin=__Link
set Assemblies=-a System.Private.CoreLib

IF defined DoLink (
    IF NOT EXIST !ILLINK! (
      ECHO ILLink executable [%ILLINK%] Invalid
      popd
      Exit /b 1
    )

    REM Clean up old Linked binaries, if any
    IF EXIST %LinkBin% rmdir /s /q %LinkBin%

    REM Remove Native images, since the goal is to run from Linked binaries
    del /q /f *.ni.* 2> nul

    REM Use hints for reflection roots, if provided in $(ReflectionRootsXml)
    IF EXIST $(ReflectionRootsXml) set ReflectionRoots=-x $(ReflectionRootsXml)

    REM Include all .exe files in this directory as entry points (some tests had multiple .exe file modules)
    FOR /F "delims=" %%E IN ('dir /b *.exe *.dll') DO SET Assemblies=!Assemblies! -a %%~nE

    REM Run dotnet-linker
    REM Run the Linker such that all assemblies except System.Private.Corlib.dll are linked
    REM Debug symbol generation needs some fixes, and is currently omitted.
    REM Once this is fixed, add -b true option.
    ECHO %ILLINK% -out %LinkBin% -d %CORE_ROOT% -c link -l none -t !Assemblies! !ReflectionRoots!
    %ILLINK% -out %LinkBin% -d %CORE_ROOT% -c link -l none -t !Assemblies! !ReflectionRoots!
    IF NOT "!ERRORLEVEL!"=="0" (
      ECHO ILLINK FAILED !ERRORLEVEL!
      IF NOT defined KeepLinkedBinaries (
          IF EXIST %LinkBin% rmdir /s /q %LinkBin%
      )
      popd
      Exit /b 1
    )

    REM Copy CORECLR native binaries and the test watcher to %LinkBin%, so that we can run the test based on that directory
    copy %CORE_ROOT%\clrjit.dll %LinkBin% > nul 2> nul
    copy %CORE_ROOT%\coreclr.dll %LinkBin% > nul 2> nul
    copy %CORE_ROOT%\mscorrc.dll %LinkBin% > nul 2> nul
    copy %CORE_ROOT%\CoreRun.exe %LinkBin% > nul 2> nul
    copy %CORE_ROOT%\watchdog.exe %LinkBin% > nul 2> nul

    REM Copy some files that may be arguments
    copy *.txt %LinkBin% > nul 2> nul

    set ExePath=%LinkBin%\$(InputAssemblyName)
    set CORE_ROOT=%scriptPath%\%LinkBin%
)
]]>
          </BatchLinkerTestLaunchCmds>
          <BatchLinkerTestCleanupCmds>
              <![CDATA[
REM Clean up the LinkBin directories after test execution.
REM Otherwise, RunTests may run out of disk space.

if defined DoLink (
    if not defined KeepLinkedBinaries (
        IF EXIST %LinkBin% rmdir /s /q %LinkBin%
    )
)
]]>
          </BatchLinkerTestCleanupCmds>
      </PropertyGroup>
      <PropertyGroup>
      <CLRTestRunFile Condition="'$(CLRTestIsHosted)'=='true'">"%CORE_ROOT%\corerun.exe" $(CoreRunArgs) %__DotEnvArg%</CLRTestRunFile>
      <WatcherRunFile>"%CORE_ROOT%\watchdog.exe" %_WatcherTimeoutMins%</WatcherRunFile>

      <BatchCopyCoreShimLocalCmds Condition="'$(CLRTestScriptLocalCoreShim)' == 'true'"><![CDATA[
REM Local CoreShim requested - see MSBuild property 'CLRTestScriptLocalCoreShim'
ECHO Copying '%CORE_ROOT%\CoreShim.dll'...
COPY /y %CORE_ROOT%\CoreShim.dll .
      ]]></BatchCopyCoreShimLocalCmds>
      <BatchCLRTestLaunchCmds Condition="'$(CLRTestKind)' == 'BuildAndRun' And $(TargetOS) != 'android'">
    <![CDATA[
$(BatchLinkerTestLaunchCmds)
$(BatchCopyCoreShimLocalCmds)

IF NOT "%CLRCustomTestLauncher%"=="" (
  set LAUNCHER=call %CLRCustomTestLauncher% %scriptPath%
) ELSE IF %_RunWithWatcher% EQU 1 (
  set LAUNCHER=$(WatcherRunFile) $(CLRTestRunFile)
) ELSE (
  set LAUNCHER=%_DebuggerFullPath% $(CLRTestRunFile)
)

if defined RunCrossGen2 (
  call :TakeLock
)

ECHO %LAUNCHER% %ExePath% %CLRTestExecutionArguments%
%LAUNCHER% %ExePath% %CLRTestExecutionArguments%
set CLRTestExitCode=!ERRORLEVEL!
if defined RunCrossGen2 (
  call :ReleaseLock
)
$(BatchLinkerTestCleanupCmds)
      ]]></BatchCLRTestLaunchCmds>
      <BatchCLRTestLaunchCmds Condition="'$(CLRTestKind)' == 'BuildAndRun' And $(TargetOS) == 'android'">
    <![CDATA[
REM run Android app
IF NOT "%__TestDotNetCmd%"=="" (
  set __Command=%__TestDotNetCmd%
) ELSE (
  set __Command=dotnet
)

IF NOT "%XHARNESS_CLI_PATH%"=="" (
  REM When running in CI, we only have the .NET runtime available
  REM We need to call the XHarness CLI DLL directly via dotnet exec
  set HARNESS_RUNNER=exec "%XHARNESS_CLI_PATH%"
) ELSE (
  set HARNESS_RUNNER=xharness
)

%__Command% %HARNESS_RUNNER% android run --instrumentation="net.dot.MonoRunner" --package-name="net.dot.%__Category%" --output-directory="%__OutputDir%" --arg=entrypoint:libname=$(MsBuildProjectName).dll --expected-exit-code=100 -v
set CLRTestExitCode=!ERRORLEVEL!
set CLRTestExpectedExitCode=0
    ]]></BatchCLRTestLaunchCmds>

      <BatchCLRTestLaunchCmds Condition="'$(CLRTestKind)' == 'RunOnly'"><![CDATA[
$(BatchCopyCoreShimLocalCmds)
ECHO  cmd /c $(InputAssemblyName)
cmd /c $(InputAssemblyName)
set CLRTestExitCode=!ERRORLEVEL!
set CLRTestExpectedExitCode=0
      ]]></BatchCLRTestLaunchCmds>
    </PropertyGroup>

    <PropertyGroup>
      <BatchEnvironmentVariables>
@(CLRTestBatchEnvironmentVariable -> 'set %(Identity)=%(Value)', '%0d%0a')
@(CLRTestEnvironmentVariable -> 'set %(Identity)=%(Value)', '%0d%0a')
      </BatchEnvironmentVariables>
    </PropertyGroup>

    <Message Text="MSBuildProjectDirectory:$(MSBuildProjectDirectory)" />
    <Message Text="_CLRTestToRunFileFullPath:$(_CLRTestToRunFileFullPath)"/>
    <Message Text="CLRTestRunFile:$(CLRTestRunFile)" />

    <ItemGroup>
      <_RequiredProperties Include="CLRTestRunFile">
        <Value>$(CLRTestRunFile)</Value>
      </_RequiredProperties>
    </ItemGroup>

    <!-- Raise an error if any value in _RequiredProperties is missing  -->
    <Error Condition=" '%(_RequiredProperties.Value)'=='' "
      Text="Missing required test property [%(_RequiredProperties.Identity)]. Something isn't plumbed through correctly.  Contact $(_CLRTestBuildSystemOwner)." />
    <PropertyGroup>
      <!--
      This generates the script portion to parse all of the command line arguments.
      The Command metadata for BatchCLRTestExecutionScriptArgument items is executed
      when the argument is found.
      -->
      <BatchCLRTestArgPrep><![CDATA[
REM Parse Command Line
:NextArg

IF /I '%1' == '-?'     GOTO :USAGE
IF /I '%1' == '/?'     GOTO :USAGE
IF /I '%1' == '-h'     GOTO :USAGE
IF /I '%1' == '/h'     GOTO :USAGE

@(BatchCLRTestExecutionScriptArgument -> 'set cond=0
IF /I [%1] == [-%(Identity)] set cond=1
IF /I [%1] == [/%(Identity)] set cond=1
IF %cond% EQU 1 (
%(Command)
    shift
    IF /I [%(HasParam)] == [true] shift
    goto NextArg
)','
')

:ExtraArgs
if NOT "%1" == "" (
    set CLRTestExecutionArguments=%CLRTestExecutionArguments% %1
    shift
    goto :ExtraArgs
)

goto ArgsDone

:USAGE
ECHO.Usage
ECHO %0 @(BatchCLRTestExecutionScriptArgument -> '[-%(Identity) %(ParamName)]', ' ')
ECHO.
ECHO                         - OPTIONS -
@(BatchCLRTestExecutionScriptArgument -> 'ECHO -%(Identity) %(ParamName)
ECHO      %(Description)', '
')
popd
Exit /b 1

:ArgsDone
$(BatchCLRTestArgPrep)
      ]]></BatchCLRTestArgPrep>
      <!-- NOTE! semicolons must be escaped with %3B boooo -->
      <_CLRTestExecutionScriptText>
  <![CDATA[
@ECHO OFF
setlocal ENABLEDELAYEDEXPANSION
set "lockFolder=%~dp0\lock"
pushd %~dp0
set "scriptPath=%~dp0"
set /A _RunWithWatcher=0
set /A _WatcherTimeoutMins=10

IF NOT "%__TestCollectionTimeoutMins%"=="" (
    set /A _WatcherTimeoutMins=%__TestCollectionTimeoutMins%
)

REM The watcher needs a bit of time to start up, capture dumps, clean up, and so on.
REM Because of this, we pass a smaller timeout than the test collection one.
REM For simplicity purposes, we will assume there are no work items with just
REM a one-minute max timeout.
IF %_WatcherTimeoutMins% GTR 1 (
    set /A _WatcherTimeoutMins-=1
)

$(BatchCLRTestArgPrep)
$(BatchCLRTestExitCodePrep)

REM The __TestEnv variable may be used to specify something to run before the test.
IF NOT "%__TestEnv%"=="" (
    call %__TestEnv%
    IF NOT "!ERRORLEVEL!"=="0" (
        ECHO CALLING __TestEnv SCRIPT FAILED
        popd
        Exit /b 1
    )
)

$(BatchCLRTestEnvironmentCompatibilityCheck)

$(IlasmRoundTripBatchScript)

$(SuperPMICollectionBatchScript)

REM Allow test environment variables or precommands to override the ExePath
set ExePath=$(InputAssemblyName)
set TestExclusionListPath=%CORE_ROOT%\TestExclusionList.txt

REM Environment Variables
$(BatchEnvironmentVariables)

REM Precommands
$(CLRTestBatchPreCommands)
REM Launch
$(BatchCLRTestLaunchCmds)
REM PostCommands
$(CLRTestBatchPostCommands)
$(BatchCLRTestExitCodeCheck)
      ]]></_CLRTestExecutionScriptText>
    </PropertyGroup>
    <!-- Write the file -->
    <WriteLinesToFile
      File="$(OutputPath)\$(AssemblyName).cmd"
      Lines="$(_CLRTestExecutionScriptText)"
      Overwrite="true" />
  </Target>

</Project>
